#pragma once
#include "texture.h"

namespace krryn{
namespace system_imp{
	template<unsigned int _bits>
	unsigned int morton(unsigned int x, unsigned int y)
	{
#if 0
		unsigned int mask = 1 << (_bits - 1);
		unsigned int result = 0;

		for(unsigned int i = _bits; i; i--)
		{
			result |= x & mask; result <<= 1;
			result |= y & mask; mask >>= 1;
		}

		return result;
#else

									
		// from http://graphics.stanford.edu/~seander/bithacks.html
		static const unsigned int B[] = {0x55555555, 0x33333333, 0x0F0F0F0F, 0x00FF00FF};

		x = (x | (x << 8)) & B[3]; // x = ---- ---- ---- ---- fedc ba98 7654 3210
		x = (x | (x << 4)) & B[2]; // x = ---- ---- fedc ba98 ---- ---- 7654 3210
		x = (x | (x << 2)) & B[1]; // x = ---- fedc ---- ba98 ---- 7654 ---- 3210
		x = (x | (x << 1)) & B[0]; // x = --fe --dc --ba --98 --76 --54 --32 --10
								   // x = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0
		y = (y | (y << 8)) & B[3];
		y = (y | (y << 4)) & B[2];
		y = (y | (y << 2)) & B[1];
		y = (y | (y << 1)) & B[0];

		return x | (y << 1);
#endif
	}

	class texture_software : public view::texture{
	public:
		texture_software(view::canvas *a_Canvas, size_t a_TextureUnit) : texture(a_Canvas, a_TextureUnit){
			view::canvas *l_Canvas = new view::canvas(a_Canvas->get_width(), a_Canvas->get_height());

			for(int x = 0; x < a_Canvas->get_width(); x++){
				for(int y = 0; y < a_Canvas->get_height(); y++){
#if USE_MORTON_TEXTURE_SWIZZLE
					l_Canvas->get_buffer()[morton<16>(x, y)] = 
						a_Canvas->get_buffer()[x + y * a_Canvas->get_width()];
#else
					l_Canvas->get_buffer()[x + y * a_Canvas->get_width()] = 
						a_Canvas->get_buffer()[x + y * a_Canvas->get_width()];
#endif
				}
			}

			std::list<view::canvas *> l_MipMaps = l_Canvas->generate_mip_maps(view::canvas::scale_linear);

			m_Levels = (int)l_MipMaps.size();
			m_MipMaps = new view::canvas*[m_Levels];

			std::list<view::canvas*>::iterator i = l_MipMaps.begin();
			std::list<view::canvas*>::iterator l_End = l_MipMaps.end();

			/// put items in array for O(1) access time
			for(int n = 0; i != l_End; ++i, n++){
				m_MipMaps[n] = *i;
			}
		}

		virtual ~texture_software(){
			for(int i = 0; i < m_Levels; i++){
				delete m_MipMaps[i];
			}

			delete[] m_MipMaps;
		}

		view::canvas *get_canvas_at(int a_MipMapLevel){
			if(a_MipMapLevel >= m_Levels){
				a_MipMapLevel = m_Levels - 1;
			}else if(a_MipMapLevel < 0){
				a_MipMapLevel = 0;
			}

			return m_MipMaps[a_MipMapLevel];
		}

		virtual void enable() {}
		virtual void disable() {}

	private:
		int m_Levels;
		view::canvas **m_MipMaps;
	};
}
}